package com.itao.springboot.aop;

import com.itao.springboot.annotation.LogAnn;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
public class LogAspect {
    /**
     * 用于获取方法参数定义名字.
     */
    private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    /**
     * 用于SpEL表达式解析.
     */
    private static final SpelExpressionParser parser = new SpelExpressionParser();

    @Pointcut("@annotation(com.itao.springboot.annotation.LogAnn)")
    public void logAnn() {
    }

    @Around("logAnn()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取目标类
        Object target = joinPoint.getThis();
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
        // 获取方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取参数
        Object[] args = joinPoint.getArgs();
        // 可解决代理对象问题
        Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
        LogAnn logAnn = method.getAnnotation(LogAnn.class);
        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(target, targetMethod, args, parameterNameDiscoverer);
        // 处理注解上的值
        String bizNo = logAnn.bizNo();
        String bizNoValue = getExpValue(context, bizNo);
        log.info("bizNo:{}", bizNoValue);
        String operator = logAnn.operator();
        String operatorValue = getExpValue(context, operator);
        log.info("operator:{}", operatorValue);
        // to do 
        return joinPoint.proceed();
    }

    private String getExpValue(MethodBasedEvaluationContext context, String expr) {
        Expression expression = parser.parseExpression(expr);
        return (String) expression.getValue(context);
    }
}
